﻿#include "stdafx.h"
#include "sbcsx.h"
#include "sbctn.h"

//////////////////////////////////////////////////////////////////////////
// 스트림
KIMPL_SUBCLASS_ABSTRACT(kStream, "Stream");

//
kStream::kStream(kint flags /* = 0 */)
: _flags(flags)
{

}

//
kStream::~kStream()
{
	if (_mount)
		_mount->Unload();
}

//
void kStream::SetMount(kMount* mnt)
{
	kobj_unload(_mount); 
	_mount = mnt; 
}


//////////////////////////////////////////////////////////////////////////
// 메모리 스트림
KIMPL_SUBCLASS(kMemoryStream, "MemoryStream");

//
class kMemoryStreamFake : public kMemoryStream
{
public:
	void Expand(ksize_t size)
	{
		ksize_t capa = _capa == 0 ? 64 : _capa;
		ksize_t cmp = _capa - _loc;

		if (cmp < size)
		{
			cmp = _loc + size;

			while (capa < cmp)
				capa <<= 1;

			_data = k_renew(_data, capa, kbyte);
			_capa = capa;
		}
	}

	void InitCapa(kint capa)
	{
		if (capa > 0)
		{
			_capa = capa;
			_data = k_new(capa, kbyte);
		}
	}

	void InitExtern(kpointer buffer, kint capacity, kint size)
	{
		_data = (kbyte*)buffer;
		_capa = capacity;
		_size = size;
	}
};

//
kMemoryStream::kMemoryStream()
: super_type(KFF_ALL)
{
}

//
kMemoryStream::~kMemoryStream()
{
	k_delete(_data);
}

//
kint kMemoryStream::Read(kpointer buffer, kint offset, kint size)
{
	k_return_value_if_fail(buffer, -1);
	k_return_value_if_fail(size > 0, 0);

	ksize_t n = _size - _loc;
	size = (kint)K_MIN(n, (ksize_t)size);

	if (size == 0)
		return 0;
	else
	{
		memcpy((kbyte*)buffer + offset, _data + _loc, size);
		_loc += size;

		return size;
	}
}

//
kint kMemoryStream::Write(kconstpointer buffer, kint offset, kint size)
{
	k_return_value_if_fail(buffer, -1);
	k_return_value_if_fail(size > 0, 0);

	static_cast<kMemoryStreamFake*>(this)->Expand(size);

	memcpy(_data + _loc, (const kbyte*)buffer + offset, size);
	_loc += size;

	if (_size < _loc)
		_size = _loc;

	return size;
}

//
klong kMemoryStream::GetSize()
{
	return _size;
}

//
kulong kMemoryStream::Tell()
{
	return _loc;
}

//
kbool kMemoryStream::Seek(klong fakeoffset, kint org)
{
	ksize_t size;
	ksize_t offset = (ksize_t)fakeoffset;

	switch (org)
	{
		case KSEEK_BEGIN:
			size = offset;
			break;

		case KSEEK_CUR:
			size = _loc + offset;
			break;

		case KSEEK_END:
			size = _size + offset;
			break;

		default:
			return FALSE;
	}

	if (size > _size)
		_loc = _size;
	else
		_loc = size;

	return true;
}

//
kbool kMemoryStream::Flush()
{
	return true;
}

//
kbool kMemoryStream::SetSize(ksize_t fakesize)
{
	k_return_value_if_fail(fakesize > 0, false);

	ksize_t size = fakesize;

	if (size < _size)
		_size = size;
	else if (size < _capa)
		_size = size;
	else
	{
		static_cast<kMemoryStreamFake*>(this)->Expand(size - _loc);
		_size = size;
	}

	if (_loc < size)
		_loc = size;

	return true;
}

//
kbool kMemoryStream::Dump(kStream* stream)
{
	k_return_value_if_fail(stream, false);
	k_return_value_if_fail(this != stream, false);

	return stream->Write(_data, 0, (kint)_size) == (kint)_size;
}

//
kpointer kMemoryStream::ReplaceExtern(kpointer buffer, kint capacity, kint size)
{
	kpointer ret = _data;

	if (buffer && capacity > 0 && size > 0 && size <= capacity)
	{
		_data = (kbyte*)buffer;
		_capa = capacity;
		_size = size;
		_loc = size;
	}
	else
	{
		_data = NULL;
		_capa = 0;
		_size = 0;
		_loc = 0;
	}

	return ret;
}

//
kMemoryStream* kMemoryStream::New(kint initcapa/*=0*/)
{
	kMemoryStream* self = kobj_create(kMemoryStream);
	static_cast<kMemoryStreamFake*>(self)->InitCapa(initcapa);
	return self;
}

//
kMemoryStream* kMemoryStream::NewExtern(kpointer buffer, kint capacity, kint size)
{
	kMemoryStream* self = kobj_create(kMemoryStream);
	static_cast<kMemoryStreamFake*>(self)->InitExtern(buffer, capacity, size);
	return self;
}


//////////////////////////////////////////////////////////////////////////
// 파일 스트림
KIMPL_SUBCLASS(kFileStream, "FileStream");

//
class kFileStreamFake : public kFileStream
{
public:
	void SetFile(kFile* file, const kchar* filename)
	{
		_file = file;
		_flags = k_file_flags(file, 0);
		_desc = filename;
	}

	void SetFile(kFile* file, const kchar* filename, kMount* mount)
	{
		_file = file;
		_flags = k_file_flags(file, 0);
		_desc = filename;

		_mount = mount;
		mount->Load();
	}
};

//
kFileStream::kFileStream()
{

}

//
kFileStream::~kFileStream()
{
	if (_file)
		k_file_delete(_file);
}

//
kint kFileStream::Read(kpointer buffer, kint offset, kint size)
{
	return k_file_read(_file, buffer, offset, size);
}

//
kint kFileStream::Write(kconstpointer buffer, kint offset, kint size)
{
	return k_file_write(_file, buffer, offset, size);
}

//
klong kFileStream::GetSize()
{
	return (klong)k_file_size(_file);
}

//
kulong kFileStream::Tell()
{
	return (klong)k_file_tell(_file);
}

//
kbool kFileStream::Seek(klong offset, kint org)
{
	return k_file_seek(_file, offset, org) >= 0;
}

//
kbool kFileStream::Flush()
{
	return k_file_flush(_file) != 0;
}

//
kFileStream* kFileStream::New(const kchar* filename, const kchar* mode)
{
	kFile* file = k_file_new(filename, mode);

	if (!file)
		return NULL;

	kFileStream* self = kobj_create(kFileStream);
	static_cast<kFileStreamFake*>(self)->SetFile(file, filename);
	return self;
}

//
kFileStream* kFileStream::NewMount(const kchar* filename, const kchar* mode, kMount* mount)
{
	k_return_value_if_fail(filename, NULL);
	k_return_value_if_fail(mount, NULL);

	tpl::BlueStr<K_MAX_PATH> bs(mount->GetDesc());

	if (*filename != '/' && *filename != '\\')
		bs += mount->GetDir();

	bs += filename;

	kFile* file = k_file_new(bs.Data(), mode);

	if (!file)
		return NULL;

	kFileStream* self = kobj_create(kFileStream);
	static_cast<kFileStreamFake*>(self)->SetFile(file, filename, mount);
	return self;
}


//////////////////////////////////////////////////////////////////////////
// 간접 스트림
KIMPL_SUBCLASS(kIndirectStream, "IndirectStream");

//
kIndirectStream::kIndirectStream()
: super_type(KFF_RDONLY)
{
}

//
kIndirectStream::~kIndirectStream()
{
	k_file_delete(_file);
}

//
kint kIndirectStream::Read(kpointer buffer, kint offset, kint size)
{
	kint ret = k_file_read(_file, buffer, offset, size);

	_loc += ret;

	return ret;
}

//
klong kIndirectStream::GetSize()
{
	return _size;
}

//
kulong kIndirectStream::Tell()
{
	return _loc;
}

//
kbool kIndirectStream::Seek(klong fakeoffset, kint org)
{
	ksize_t offset = _indi + (kssize_t)fakeoffset;

	klong newpos = k_file_seek(_file, offset, org);
	if (newpos < 0)
		return false;

	_loc = (ksize_t)newpos - _indi;

	if (_loc > _size)
		_loc = _size;

	return true;
}

//
kIndirectStream* kIndirectStream::NewExtern(kFile* file, ksize_t indi, ksize_t size)
{
	k_return_value_if_fail(file, NULL);
	k_return_value_if_fail(size > 0, NULL);

	kIndirectStream* self = kobj_create(kIndirectStream);
	self->_file = file;
	self->_indi = indi;
	self->_size = size;

	k_file_seek(file, indi, KSEEK_BEGIN);

	return self;
}



